package com.chenfan.ccp.util.tool;

import com.chenfan.ccp.executor.SmartExecutorService;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

public class HttpUtil {

    private static final String GET = "GET";
    private static final String POST = "POST";
    private volatile static int READ_TIME = 5000;
    private static ExecutorService executorService;
    private volatile static int CONNECT_TIME = 5000;

    static {
        if (executorService != null) {

            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {

                    executorService.shutdown();
                }

            });
        }

    }

    private HttpUtil() {

    }

    public static ExecutorService getExecutorService() {
        if (executorService == null) {
            executorService = SmartExecutorService.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
        }
        return executorService;
    }

    //同步start----------------
    public static String get(String url, String body) {

        return httpRequestToString(url, new HashMap<>(), GET, body);
    }

    public static String get(String url, Map body) {

        return httpRequestToString(url, new HashMap<>(), GET, map2String(body));
    }

    public static String get(Map<String, String> headers, String url, String body) {

        return httpRequestToString(url, headers, GET, body);
    }

    public static String get(Map<String, String> headers, String url, Map body) {

        return httpRequestToString(url, headers, GET, map2String(body));
    }

    public static String get(Map<String, String> headers, String url) {

        return httpRequestToString(url, headers, GET, null);
    }

    public static String get(String url) {

        return httpRequestToString(url, new HashMap<>(), GET, null);
    }

    public static File get2File(String url, String body) {

        return httpRequestToFile(url, new HashMap<>(), GET, body);
    }

    public static File get2File(String url, Map body) {

        return httpRequestToFile(url, new HashMap<>(), GET, map2String(body));
    }

    public static File get2File(Map<String, String> headers, String url, String body) {

        return httpRequestToFile(url, headers, GET, body);
    }

    public static File get2File(Map<String, String> headers, String url, Map body) {

        return httpRequestToFile(url, headers, GET, map2String(body));
    }

    public static File get2File(Map<String, String> headers, String url) {

        return httpRequestToFile(url, headers, GET, null);
    }

    public static File get2File(String url) {

        return httpRequestToFile(url, new HashMap<>(), GET, null);
    }

    public static String post(String url, String body) {

        return httpRequestToString(url, new HashMap<>(), POST, body);
    }

    public static String post(String url, Map body) {

        return httpRequestToString(url, new HashMap<>(), POST, map2String(body));
    }

    public static String post(Map<String, String> headers, String url, String body) {

        return httpRequestToString(url, headers, POST, body);
    }

    public static String post(Map<String, String> headers, String url, Map body) {

        return httpRequestToString(url, headers, POST, map2String(body));
    }

    public static String post(Map<String, String> headers, String url) {

        return httpRequestToString(url, headers, POST, null);
    }

    public static String post(String url) {

        return httpRequestToString(url, new HashMap<>(), POST, null);
    }

    public static File post2File(String url, String body) {

        return httpRequestToFile(url, new HashMap<>(), POST, body);
    }

    public static File post2File(String url, Map body) {

        return httpRequestToFile(url, new HashMap<>(), POST, map2String(body));
    }

    public static File post2File(Map<String, String> headers, String url, String body) {

        return httpRequestToFile(url, headers, POST, body);
    }

    public static File post2File(Map<String, String> headers, String url, Map body) {

        return httpRequestToFile(url, headers, POST, map2String(body));
    }

    public static File post2File(Map<String, String> headers, String url) {

        return httpRequestToFile(url, headers, POST, null);
    }

    //同步end----------------

    public static File post2File(String url) {

        return httpRequestToFile(url, new HashMap<>(), POST, null);
    }

    private static void async(String path, Map<String, String> headers, String method, String body, Consumer task, boolean isFile) {

        getExecutorService().execute(() -> {
            if (isFile) {
                File file = httpRequestToFile(path, headers, method, body);
                if (task != null) {
                    task.accept(file);
                }

            } else {
                String result = httpRequestToString(path, headers, method, body);
                if (task != null) {
                    task.accept(result);
                }

            }
        });

    }

    //异步消费者start----------------
    public static void getAsync(String url, String body, Consumer task) {

        async(url, new HashMap<>(), GET, body, task, false);
    }

    public static void getAsync(String url, Map body, Consumer task) {

        async(url, new HashMap<>(), GET, map2String(body), task, false);
    }

    public static void getAsync(Map<String, String> headers, String url, String body, Consumer task) {

        async(url, headers, GET, body, task, false);
    }

    public static void getAsync(Map<String, String> headers, String url, Map body, Consumer task) {

        async(url, headers, GET, map2String(body), task, false);
    }

    public static void getAsync(Map<String, String> headers, String url, Consumer task) {

        async(url, headers, GET, null, task, false);

    }

    public static void getAsync(String url, Consumer task) {

        async(url, new HashMap<>(), GET, null, task, false);
    }

    public static void get2FileAsync(String url, String body, Consumer task) {

        async(url, new HashMap<>(), GET, body, task, true);
    }

    public static void get2FileAsync(String url, Map body, Consumer task) {

        async(url, new HashMap<>(), GET, map2String(body), task, true);

    }

    public static void get2FileAsync(Map<String, String> headers, String url, String body, Consumer task) {

        async(url, headers, GET, body, task, true);

    }

    public static void get2FileAsync(Map<String, String> headers, String url, Map body, Consumer task) {

        async(url, headers, GET, map2String(body), task, true);
    }

    public static void get2FileAsync(Map<String, String> headers, String url, Consumer task) {

        async(url, headers, GET, null, task, true);
    }

    public static void get2FileAsync(String url, Consumer task) {

        async(url, new HashMap<>(), GET, null, task, true);
    }

    public static void postAsync(String url, String body, Consumer task) {

        async(url, new HashMap<>(), POST, body, task, false);
    }

    public static void postAsync(String url, Map body, Consumer task) {

        async(url, new HashMap<>(), POST, map2String(body), task, false);
    }

    public static void postAsync(Map<String, String> headers, String url, String body, Consumer task) {

        async(url, headers, POST, body, task, false);
    }

    public static void postAsync(Map<String, String> headers, String url, Map body, Consumer task) {

        async(url, headers, POST, map2String(body), task, false);

    }

    public static void postAsync(Map<String, String> headers, String url, Consumer task) {

        async(url, headers, POST, null, task, false);
    }

    public static void postAsync(String url, Consumer task) {

        async(url, new HashMap<>(), POST, null, task, false);

    }

    public static void post2FileAsync(String url, String body, Consumer task) {

        async(url, new HashMap<>(), POST, body, task, true);
    }

    public static void post2FileAsync(String url, Map body, Consumer task) {

        async(url, new HashMap<>(), POST, map2String(body), task, true);

    }

    public static void post2FileAsync(Map<String, String> headers, String url, String body, Consumer task) {

        async(url, headers, POST, body, task, true);
    }

    public static void post2FileAsync(Map<String, String> headers, String url, Map body, Consumer task) {
        async(url, headers, POST, map2String(body), task, true);

    }

    public static void post2FileAsync(Map<String, String> headers, String url, Consumer task) {
        async(url, headers, POST, null, task, true);
    }

    //异步消费者end----------------

    public static void post2FileAsync(String url, Consumer task) {

        async(url, new HashMap<>(), POST, null, task, true);

    }

    //异步无消费者start----------------
    public static void getAsync(String url, String body) {

        async(url, new HashMap<>(), GET, body, null, false);
    }

    public static void getAsync(String url, Map body) {

        async(url, new HashMap<>(), GET, map2String(body), null, false);
    }

    public static void getAsync(Map<String, String> headers, String url, String body) {

        async(url, headers, GET, body, null, false);
    }

    public static void getAsync(Map<String, String> headers, String url, Map body) {

        async(url, headers, GET, map2String(body), null, false);
    }

    public static void getAsync(Map<String, String> headers, String url) {

        async(url, headers, GET, null, null, false);

    }

    public static void getAsync(String url) {

        async(url, new HashMap<>(), GET, null, null, false);
    }

    public static void get2FileAsync(String url, String body) {

        async(url, new HashMap<>(), GET, body, null, true);
    }

    public static void get2FileAsync(String url, Map body) {

        async(url, new HashMap<>(), GET, map2String(body), null, true);

    }

    public static void get2FileAsync(Map<String, String> headers, String url, String body) {

        async(url, headers, GET, body, null, true);

    }

    public static void get2FileAsync(Map<String, String> headers, String url, Map body) {

        async(url, headers, GET, map2String(body), null, true);
    }

    public static void get2FileAsync(Map<String, String> headers, String url) {

        async(url, headers, GET, null, null, true);
    }

    public static void get2FileAsync(String url) {

        async(url, new HashMap<>(), GET, null, null, true);
    }

    public static void postAsync(String url, String body) {

        async(url, new HashMap<>(), POST, body, null, false);
    }

    public static void postAsync(String url, Map body) {

        async(url, new HashMap<>(), POST, map2String(body), null, false);
    }

    public static void postAsync(Map<String, String> headers, String url, String body) {

        async(url, headers, POST, body, null, false);
    }

    public static void postAsync(Map<String, String> headers, String url, Map body) {

        async(url, headers, POST, map2String(body), null, false);

    }

    public static void postAsync(Map<String, String> headers, String url) {

        async(url, headers, POST, null, null, false);
    }

    public static void postAsync(String url) {

        async(url, new HashMap<>(), POST, null, null, false);

    }

    public static void post2FileAsync(String url, String body) {

        async(url, new HashMap<>(), POST, body, null, true);
    }

    public static void post2FileAsync(String url, Map body) {

        async(url, new HashMap<>(), POST, map2String(body), null, true);

    }

    public static void post2FileAsync(Map<String, String> headers, String url, String body) {

        async(url, headers, POST, body, null, true);
    }

    public static void post2FileAsync(Map<String, String> headers, String url, Map body) {
        async(url, headers, POST, map2String(body), null, true);

    }

    public static void post2FileAsync(Map<String, String> headers, String url) {
        async(url, headers, POST, null, null, true);
    }


    //异步无消费者start----------------

    public static void post2FileAsync(String url) {

        async(url, new HashMap<>(), POST, null, null, true);

    }

    private static String map2String(Map params) {
        StringBuilder data = new StringBuilder();

        for (Object param : params.entrySet()) {
            if (param instanceof Map.Entry) {
                Map.Entry entry = (Map.Entry) param;
                if (data.length() != 0) {
                    data.append('&');
                }

                try {
                    data.append(URLEncoder.encode(String.valueOf(entry.getKey()), "utf-8"));
                } catch (UnsupportedEncodingException e) {

                }


                data.append('=');
                try {
                    data.append(URLEncoder.encode(String.valueOf(entry.getValue()), "utf-8"));
                } catch (UnsupportedEncodingException e) {

                }
            }

        }
        return data.toString();
    }

    private static String httpRequestToString(String path, Map<String, String> headers, String method, String body) {

        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        String response = "";
        URLConnection conn;
        InputStream inputStream = null;
        try {
            conn = baseHttpRequest(path, headers, method, body);

            if (conn == null) {
                return response;
            }
            inputStream = conn.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
            bufferedReader = new BufferedReader(inputStreamReader);
            String str;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            response = buffer.toString();

            conn.getHeaderFields();

            convertHeader(conn.getHeaderFields(), headers);

        } catch (Exception e) {
            e.printStackTrace();

        } finally {


            try {
                if (bufferedReader != null) {

                    bufferedReader.close();
                }
                if (inputStreamReader != null) {

                    inputStreamReader.close();
                }
                if (inputStream != null) {

                    inputStream.close();
                }
            } catch (IOException execption) {

            }
        }
        return response;
    }

    private static File httpRequestToFile(String path, Map<String, String> headers, String method, String body) {

        OutputStream os = null;
        BufferedInputStream bis = null;

        File tmpFile = null;
        URLConnection conn;
        InputStream inputStream = null;
        try {
            conn = baseHttpRequest(path, headers, method, body);
            if (conn == null) {
                return tmpFile;
            }

            String fileName = conn.getURL().getFile();
            if (fileName == null) {
                fileName = UUID.randomUUID().toString();
                tmpFile = File.createTempFile(fileName, fileName);
            } else {
                fileName = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('?'));
                tmpFile = new File(System.getProperty("java.io.tmpdir"), fileName);
                if (!tmpFile.exists()) {
                    tmpFile.createNewFile();
                }
            }


            inputStream = conn.getInputStream();
            bis = new BufferedInputStream((inputStream));
            os = new FileOutputStream(tmpFile);
            int len;
            byte[] arr = new byte[1024];
            while ((len = bis.read(arr)) != -1) {
                os.write(arr, 0, len);
                os.flush();
            }
            os.close();

            convertHeader(conn.getHeaderFields(), headers);

        } catch (Exception e) {

        } finally {

            try {
                os.close();
                bis.close();
                inputStream.close();
            } catch (IOException execption) {

            }
        }
        return tmpFile;
    }

    private static void convertHeader(Map<String, List<String>> sourceHeader
            , Map<String, String> targetHeader) {
        targetHeader.clear();
        StringBuilder builder;
        for (Map.Entry<String, List<String>> entry : sourceHeader.entrySet()) {
            builder = new StringBuilder();
            for (String value : entry.getValue()) {

                if (builder.length() > 0) {
                    builder.append(";");
                }

                builder.append(value);
            }
            targetHeader.put(entry.getKey(), builder.toString());

        }


    }

    private static URLConnection baseHttpRequest(String requestUrl, Map<String, String> headers,
                                                 String requestMethod, String outputStr) {
        try {
            SSLSocketFactory ssf = null;

            if (requestUrl.startsWith("https")) {
                TrustManager[] tm = {new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }};
                SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
                sslContext.init(null, tm, new java.security.SecureRandom());
                // 从上述SSLContext对象中得到SSLSocketFactory对象
                ssf = sslContext.getSocketFactory();


            }


            if (GET.equals(requestMethod)
                    && outputStr != null && !outputStr.isEmpty()) {
                if (requestUrl.contains("?")) {
                    requestUrl = requestUrl + "&" + outputStr;
                } else {
                    requestUrl = requestUrl + "?" + outputStr;
                }
                outputStr = null;
            }

            URL url = new URL(requestUrl);

            if (requestUrl.startsWith("https")) {
                HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
                conn.setSSLSocketFactory(ssf);
                // 设置请求方式（GET/POST）
                conn.setRequestMethod(requestMethod);
                conn.setHostnameVerifier((hostname, session) -> true);
                return convertUrlConnection(conn, outputStr, headers);
            } else {
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                // 设置请求方式（GET/POST）
                conn.setRequestMethod(requestMethod);
                return convertUrlConnection(conn, outputStr, headers);
            }


        } catch (Exception ce) {
            ce.printStackTrace();
            return null;
        }

    }

    public static HttpUtil connectTime(int connectTime) {
        if (connectTime > 0) {
            CONNECT_TIME = connectTime;
        }

        return getInstance();
    }

    public static HttpUtil readTime(int readTime) {
        if (readTime > 0) {
            READ_TIME = readTime;
        }

        return getInstance();
    }

    private static HttpUtil getInstance() {
        return HttpUtilHolder.INSTANCE;
    }

    private static URLConnection convertUrlConnection(URLConnection conn, String outputStr, Map<String, String> headers) throws IOException {
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        // 设置连接超时
        conn.setConnectTimeout(CONNECT_TIME);
        // 设置读取超时
        conn.setReadTimeout(READ_TIME);


        for (Map.Entry<String, String> entry : headers.entrySet()) {
            conn.setRequestProperty(entry.getKey(), entry.getValue());
        }

        if (null != outputStr && !outputStr.isEmpty()) {

            if (!outputStr.contains("&") || outputStr.startsWith("{")) {
                //json
                conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            }

            OutputStream outputStream = conn.getOutputStream();
            outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8));
            outputStream.close();

        }
        return conn;
    }

    private static class HttpUtilHolder {
        private static final HttpUtil INSTANCE = new HttpUtil();

    }


}
